import numpy as np
import random
import tkinter as tk
from tkinter import messagebox
import time

# 配置中文显示
import matplotlib
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
matplotlib.rcParams['axes.unicode_minus'] = False

class TicTacToe:
    """井字棋游戏"""
    
    def __init__(self):
        """初始化井字棋游戏"""
        # 棋盘状态: 0表示空格, 1表示玩家(X), 2表示AI(O)
        self.board = np.zeros((3, 3), dtype=int)
        # 随机决定先手
        self.player_first = random.choice([True, False])
        self.game_over = False
        self.winner = None
        
        # 记录游戏步骤
        self.moves = []
        
        # 设置GUI
        self.setup_gui()
        
        # 如果AI先手，则AI先下一步
        if not self.player_first:
            self.ai_move()
    
    def setup_gui(self):
        """设置游戏GUI"""
        self.window = tk.Tk()
        self.window.title("井字棋 - α-β剪枝算法")
        self.window.geometry("400x500")
        
        # 顶部标签
        frame_top = tk.Frame(self.window)
        frame_top.pack(pady=10)
        
        first_player = "玩家(X)" if self.player_first else "AI(O)"
        self.label_info = tk.Label(frame_top, text=f"先手: {first_player}", font=("微软雅黑", 14))
        self.label_info.pack()
        
        # 状态标签
        self.label_status = tk.Label(self.window, text="游戏进行中", font=("微软雅黑", 12))
        self.label_status.pack(pady=5)
        
        # 棋盘框架
        frame_board = tk.Frame(self.window)
        frame_board.pack(pady=10)
        
        # 棋盘按钮
        self.buttons = []
        for i in range(3):
            row = []
            for j in range(3):
                button = tk.Button(frame_board, text="", font=("微软雅黑", 24), width=4, height=2,
                                  command=lambda row=i, col=j: self.player_move(row, col))
                button.grid(row=i, column=j, padx=5, pady=5)
                row.append(button)
            self.buttons.append(row)
        
        # 底部按钮
        frame_bottom = tk.Frame(self.window)
        frame_bottom.pack(pady=20)
        
        # 添加重置按钮 - 保持当前先手/后手设置
        self.btn_reset = tk.Button(frame_bottom, text="重置", font=("微软雅黑", 12),
                                  command=self.reset_game)
        self.btn_reset.pack(side=tk.LEFT, padx=10)
        
        # 新游戏按钮 - 随机选择先手/后手
        self.btn_new_game = tk.Button(frame_bottom, text="新游戏", font=("微软雅黑", 12),
                                      command=self.new_game)
        self.btn_new_game.pack(side=tk.LEFT, padx=10)
        
        self.btn_quit = tk.Button(frame_bottom, text="退出", font=("微软雅黑", 12),
                                 command=self.window.quit)
        self.btn_quit.pack(side=tk.LEFT, padx=10)
        
        # 算法信息
        frame_info = tk.Frame(self.window)
        frame_info.pack(pady=10)
        
        self.text_info = tk.Text(frame_info, width=40, height=5, font=("微软雅黑", 10))
        self.text_info.pack()
        self.text_info.insert(tk.END, "AI使用α-β剪枝算法\n")
        self.text_info.insert(tk.END, "玩家使用X，AI使用O\n")
        self.text_info.config(state=tk.DISABLED)
    
    def reset_game(self):
        """重置游戏但保持当前先手/后手设置"""
        self.board = np.zeros((3, 3), dtype=int)
        self.game_over = False
        self.winner = None
        self.moves = []
        
        # 更新GUI
        for i in range(3):
            for j in range(3):
                self.buttons[i][j].config(text="", state=tk.NORMAL, bg="SystemButtonFace")
        
        self.label_status.config(text="游戏进行中")
        
        # 如果AI先手，则AI先下一步
        if not self.player_first:
            self.ai_move()
        
    def new_game(self):
        """开始新游戏，随机选择先手/后手"""
        self.board = np.zeros((3, 3), dtype=int)
        self.player_first = random.choice([True, False])
        self.game_over = False
        self.winner = None
        self.moves = []
        
        # 更新GUI
        for i in range(3):
            for j in range(3):
                self.buttons[i][j].config(text="", state=tk.NORMAL, bg="SystemButtonFace")
        
        first_player = "玩家(X)" if self.player_first else "AI(O)"
        self.label_info.config(text=f"先手: {first_player}")
        self.label_status.config(text="游戏进行中")
        
        # 如果AI先手，则AI先下一步
        if not self.player_first:
            self.ai_move()
    
    def player_move(self, row, col):
        """玩家下棋"""
        # 如果游戏已结束或该位置已被占用，则忽略
        if self.game_over or self.board[row, col] != 0:
            return
        
        # 玩家下棋
        self.board[row, col] = 1
        self.buttons[row][col].config(text="X", fg="blue")
        self.moves.append((row, col, 1))
        
        # 检查游戏状态
        if self.check_game_state():
            return
        
        # AI下棋
        self.window.after(500, self.ai_move)
    
    def ai_move(self):
        """AI使用α-β剪枝算法下棋"""
        if self.game_over:
            return
        
        # 显示思考中...
        self.label_status.config(text="AI思考中...")
        self.window.update()
        
        # 使用α-β剪枝算法计算最佳移动
        start_time = time.time()
        best_score = float('-inf')
        best_move = None
        alpha = float('-inf')
        beta = float('inf')
        
        # 检查是否有可能的移动
        available_moves = [(i, j) for i in range(3) for j in range(3) if self.board[i, j] == 0]
        
        if available_moves:
            # 剪枝计数器
            self.prune_count = 0
            self.node_count = 0
            
            # 评估每个可能的移动
            for i, j in available_moves:
                self.board[i, j] = 2  # AI尝试在位置(i,j)下棋
                score = self.minimax(0, False, alpha, beta)
                self.board[i, j] = 0  # 撤销尝试
                
                if score > best_score:
                    best_score = score
                    best_move = (i, j)
                
                alpha = max(alpha, best_score)
            
            # 记录AI思考信息
            elapsed_time = time.time() - start_time
            self.text_info.config(state=tk.NORMAL)
            self.text_info.delete(1.0, tk.END)
            self.text_info.insert(tk.END, f"AI使用α-β剪枝算法\n")
            self.text_info.insert(tk.END, f"玩家使用X，AI使用O\n")
            self.text_info.insert(tk.END, f"评估节点数: {self.node_count}\n")
            self.text_info.insert(tk.END, f"剪枝次数: {self.prune_count}\n")
            self.text_info.insert(tk.END, f"思考时间: {elapsed_time:.4f}秒")
            self.text_info.config(state=tk.DISABLED)
            
            # 执行AI的移动
            i, j = best_move
            self.board[i, j] = 2
            self.buttons[i][j].config(text="O", fg="red")
            self.moves.append((i, j, 2))
            
            # 更新状态标签
            self.label_status.config(text="轮到玩家")
            
            # 检查游戏状态
            self.check_game_state()
    
    def minimax(self, depth, is_maximizing, alpha, beta):
        """
        极小极大算法与α-β剪枝
        
        参数:
        - depth: 当前搜索深度
        - is_maximizing: 是否是最大化玩家(AI)的回合
        - alpha: α值，表示最大化玩家的下界
        - beta: β值，表示最小化玩家的上界
        
        返回:
        - 当前状态的评分
        """
        self.node_count += 1
        
        # 检查是否游戏结束
        result = self.check_winner()
        if result is not None:
            return self.evaluate(result)
        
        # 检查是否平局
        if np.all(self.board != 0):
            return 0
        
        if is_maximizing:
            # AI的回合 (最大化)
            max_eval = float('-inf')
            for i in range(3):
                for j in range(3):
                    if self.board[i, j] == 0:  # 空格
                        self.board[i, j] = 2  # AI尝试
                        eval = self.minimax(depth + 1, False, alpha, beta)
                        self.board[i, j] = 0  # 撤销尝试
                        max_eval = max(max_eval, eval)
                        alpha = max(alpha, eval)
                        if beta <= alpha:
                            self.prune_count += 1
                            break
                if beta <= alpha:
                    break
            return max_eval
        else:
            # 玩家的回合 (最小化)
            min_eval = float('inf')
            for i in range(3):
                for j in range(3):
                    if self.board[i, j] == 0:  # 空格
                        self.board[i, j] = 1  # 玩家尝试
                        eval = self.minimax(depth + 1, True, alpha, beta)
                        self.board[i, j] = 0  # 撤销尝试
                        min_eval = min(min_eval, eval)
                        beta = min(beta, eval)
                        if beta <= alpha:
                            self.prune_count += 1
                            break
                if beta <= alpha:
                    break
            return min_eval
    
    def evaluate(self, winner):
        """评估游戏状态"""
        if winner == 2:  # AI获胜
            return 10
        elif winner == 1:  # 玩家获胜
            return -10
        else:  # 平局
            return 0
    
    def check_winner(self):
        """检查是否有玩家获胜
        
        返回:
        - 1: 玩家获胜
        - 2: AI获胜
        - None: 没有玩家获胜
        """
        # 检查行
        for i in range(3):
            if np.all(self.board[i, :] == 1):
                return 1
            if np.all(self.board[i, :] == 2):
                return 2
        
        # 检查列
        for i in range(3):
            if np.all(self.board[:, i] == 1):
                return 1
            if np.all(self.board[:, i] == 2):
                return 2
        
        # 检查对角线
        if self.board[0, 0] == self.board[1, 1] == self.board[2, 2] and self.board[0, 0] != 0:
            return self.board[0, 0]
        if self.board[0, 2] == self.board[1, 1] == self.board[2, 0] and self.board[0, 2] != 0:
            return self.board[0, 2]
        
        return None
    
    def check_game_state(self):
        """检查游戏状态，更新界面"""
        winner = self.check_winner()
        
        # 如果有玩家获胜
        if winner is not None:
            self.game_over = True
            self.winner = winner
            
            if winner == 1:
                self.label_status.config(text="玩家获胜!")
                self.highlight_winning_line()
                messagebox.showinfo("游戏结束", "恭喜! 玩家获胜!")
            else:
                self.label_status.config(text="AI获胜!")
                self.highlight_winning_line()
                messagebox.showinfo("游戏结束", "AI获胜!")
            
            return True
        
        # 如果平局
        if np.all(self.board != 0):
            self.game_over = True
            self.label_status.config(text="游戏平局!")
            messagebox.showinfo("游戏结束", "游戏平局!")
            return True
        
        return False
    
    def highlight_winning_line(self):
        """高亮显示获胜的一行/列/对角线"""
        winner = self.winner
        if winner is None:
            return
        
        # 检查行
        for i in range(3):
            if np.all(self.board[i, :] == winner):
                for j in range(3):
                    self.buttons[i][j].config(bg="light green")
                return
        
        # 检查列
        for i in range(3):
            if np.all(self.board[:, i] == winner):
                for j in range(3):
                    self.buttons[j][i].config(bg="light green")
                return
        
        # 检查对角线
        if self.board[0, 0] == self.board[1, 1] == self.board[2, 2] == winner:
            self.buttons[0][0].config(bg="light green")
            self.buttons[1][1].config(bg="light green")
            self.buttons[2][2].config(bg="light green")
            return
        
        if self.board[0, 2] == self.board[1, 1] == self.board[2, 0] == winner:
            self.buttons[0][2].config(bg="light green")
            self.buttons[1][1].config(bg="light green")
            self.buttons[2][0].config(bg="light green")
            return
    
    def run(self):
        """运行游戏"""
        self.window.mainloop()

def main():
    """主函数"""
    game = TicTacToe()
    game.run()

if __name__ == "__main__":
    main()